Jump to content

XML DOM wrapper (COM)


eltorro
 Share

Recommended Posts

Hi all,

I#m quite new and want to use these excellent XML Tool. I tried a few things and in general I understand the logic. I have an XML file here which is not very complicated but I do not find out how to get the data out of it. The file looks like;

<settings>

<section name="a">

<entry name="a1">Text1<entry>

<entry name="a2">Text2<entry>

<entry name="a3">Text3<entry>

</section>

<section name="b">

<entry name="b1">Value1<entry>

<entry name="b2">Value2<entry>

<entry name="b3">Value3<entry>

</section>

</settings>

Much bigger but this is the logic. So what I want is to get the "Test1" or "value1".... attributes.

Can someone please help?

Thank you very much and Regrads,

Ben

Link to comment
Share on other sites

I was able to edit this once, but I can't see how to do it again, so I'll reply. I wanted to add:

Thanks for doing this!

Also I wanted to explain why I'm trying to use _XMLMdiDOM.au3. In a previous post, someone said they wanted to read nodes from one file and write them to another, and the advice they received was to use _XMLMdiDOM.au3. I want to do the same, so I picked it up and tried it. But I just noticed that it hasn't been updated since 2006 -- is it superseded by the latest _XMLDOMWrapper?

By the way, I've updated the example code -- I did include Array.au3.

If you change line 274 to

Local $retval, $fe, $objPI, $objDoc[$oIndex+1], $rootElement

from

Local $retval, $fe, $objPI, $objDoc[$oIndex], $rootElement

it'll work. I don't know if there're any more issues with the _xmlmdidom.au3... I ended up rewriting nearly the whole thing when I was using multiple xml files at once. I'd post my changes, but I'm terrible about documenting my functions or following up when people have problems.

Link to comment
Share on other sites

If you change line 274 to

Local $retval, $fe, $objPI, $objDoc[$oIndex+1], $rootElement

from

Local $retval, $fe, $objPI, $objDoc[$oIndex], $rootElement

it'll work. I don't know if there're any more issues with the _xmlmdidom.au3... I ended up rewriting nearly the whole thing when I was using multiple xml files at once. I'd post my changes, but I'm terrible about documenting my functions or following up when people have problems.

Thanks. For what I want to do, I ended up going straight to the XML DOM. What I want to do is to take an XML file with nested <concept> or <task> nodes and "burst" it into separate XML files, each one containing a single <concept> or <task> node. (This has to do with processing technical documents for use with DITA, if anyone wants to know.) This means I need to have two XML files open, and I need to copy an entire node from one to the other.

I have no idea how to use XMLDOMwrapper to do this, but it did help me learn how to use the DOM. Here's what I've sketched so far:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "dtd\ditabase.dtd">
<dita>
    <concept id="jo_001">
        <title>Data exchange overview</title>
        <conbody>
            <p>supports a number of methods for data exchange with other systems</p>
        </conbody>
        <concept id="jo_002">
            <title>Procedures for data exchange</title>
            <conbody>
                <p>The available procedures for importing and exporting information in</p>
            </conbody>
        </concept>
    </concept>
    <concept id="jo_003">
        <title>Whatever</title>
        <conbody>
            <p>Whatever else</p>
        </conbody>
    </concept>  
</dita>

$xmlDoc = ObjCreate("Msxml2.DOMdocument.3.0");
$xmlDoc.load("test.xml")
if ($xmlDoc.parseError.errorCode <> 0) Then
   $myErr = $xmlDoc.parseError;
   ConsoleWrite("You have error " & $myErr.reason);
else

 ; how to get at the concept nodes?   
 ; Here is a basic approach that can work:
   
   $nodepath = "dita/concept"
   $nodelist = $xmlDoc.selectNodes($nodepath)
   $nodes = $nodelist.length
   ConsoleWrite("Found " & $nodes & " matching " & $nodepath & @CRLF)
   $node0 = $nodelist.item(0)
   $node1 = $nodelist.item(1)
   
   ConsoleWrite("Here's one:" & @CRLF)
   ConsoleWrite($node0.xml & @CRLF)
   ConsoleWrite("Here's another:" & @CRLF)
   ConsoleWrite($node1.xml & @CRLF)
   
   $currNode = $nodelist.item(1)
   ConsoleWrite($currNode.xml & @CRLF)
EndiF

; how to write the nodes:

$xmlDoc2 = ObjCreate("Msxml2.DOMdocument.3.0")

$objPI = $xmlDoc2.createProcessingInstruction ("xml", "version=""1.0"" encoding=""UTF-8""")
$xmlDoc2.appendChild ($objPI)
$rootElement = $xmlDoc2.createElement ("dita")
$xmlDoc2.documentElement = $rootElement

$root = $xmlDoc2.documentElement 
$root.appendChild($currNode) 

ConsoleWrite($xmlDoc2.xml)
$xmlDoc2.save ("new.xml")

I've ripped out many comments, but I believe I left the program intact.

If there is a better way to do this, I'd love to know about it. I'm familiar with XML and AutoIT, more or less, but I'm new to object programming, XMLDOMwrapper, and the MSXML DOM.

Edited by AJO
Link to comment
Share on other sites

Thanks. For what I want to do, I ended up going straight to the XML DOM. What I want to do is to take an XML file with nested <concept> or <task> nodes and "burst" it into separate XML files, each one containing a single <concept> or <task> node. (This has to do with processing technical documents for use with DITA, if anyone wants to know.) This means I need to have two XML files open, and I need to copy an entire node from one to the other.

I have no idea how to use XMLDOMwrapper to do this, but it did help me learn how to use the DOM. Here's what I've sketched so far:

I've ripped out many comments, but I believe I left the program intact.

If there is a better way to do this, I'd love to know about it. I'm familiar with XML and AutoIT, more or less, but I'm new to object programming, XMLDOMwrapper, and the MSXML DOM.

OK, you made me do it. I've attached the file I use instead of _XMLMdiDOM.au3. You're on your own figuring out what my functions do, but they are at least named logically from my perspective.

I took the !DOCTYPE line out of your XML, as that's not what's giving you problems and it made my life easier without it, but here's what did what I think you want:

#include "xmlfuncs.au3"

$iSource = _XMLFileOpen( @ScriptDir & "\dita.xml" )

$aConcepts = _XMLSelectNodes( $iSource, "/dita/concept" )

For $i = 1 to $aConcepts[ 0 ]
    $sDestFileName = @ScriptDir & "\" & _XMLGetObjectAttrib( $aConcepts[ $i ], "id" ) & ".xml"
    
    $iDest = _XMLFileCreate( $sDestFileName, "dita" , True )
    
    $aOutput = _XMLSelectNodes( $iDest, "/dita" )
    
    _XMLCopyObjectAsObjectChild( $aConcepts[ $i ], $aOutput[ 1 ] )
    
    _XMLFileClose( $iDest )
Next

xmlfuncs.au3

Link to comment
Share on other sites

OK, you made me do it. I've attached the file I use instead of _XMLMdiDOM.au3. You're on your own figuring out what my functions do, but they are at least named logically from my perspective.

I took the !DOCTYPE line out of your XML, as that's not what's giving you problems and it made my life easier without it, but here's what did what I think you want:

#include "xmlfuncs.au3"

$iSource = _XMLFileOpen( @ScriptDir & "\dita.xml" )

$aConcepts = _XMLSelectNodes( $iSource, "/dita/concept" )

For $i = 1 to $aConcepts[ 0 ]
    $sDestFileName = @ScriptDir & "\" & _XMLGetObjectAttrib( $aConcepts[ $i ], "id" ) & ".xml"
    
    $iDest = _XMLFileCreate( $sDestFileName, "dita" , True )
    
    $aOutput = _XMLSelectNodes( $iDest, "/dita" )
    
    _XMLCopyObjectAsObjectChild( $aConcepts[ $i ], $aOutput[ 1 ] )
    
    _XMLFileClose( $iDest )
Next

Cool! Thanks, I have downloaded your functions and will try them out.

Thanks also for the example code. I will probably have to use a recursive function, because I need to climb into the first concept (or task or reference or topic), and if it has any child topics, climb into them, and so on, until I get a topic with no child topics, and then save that. Also I have some decisions to make about the id attribute, which is mandatory in DITA. The source file is not true DITA; some topics have IDs and some don't, but for the ones that do have ID's, I have to keep the existing value so that any cross-references still work.

I'm chipping away at all this in my spare moments, but I will certainly have a look at xmlfuncs.au3. If I add any documentation, I'l pass it on.

Edited by AJO
Link to comment
Share on other sites

Cool! Thanks, I have downloaded your functions and will try them out.

Thanks also for the example code. I will probably have to use a recursive function, because I need to climb into the first concept (or task or reference or topic), and if it has any child topics, climb into them, and so on, until I get a topic with no child topics, and then save that. Also I have some decisions to make about the id attribute, which is mandatory in DITA. The source file is not true DITA; some topics have IDs and some don't, but for the ones that do have ID's, I have to keep the existing value so that any cross-references still work.

I'm chipping away at all this in my spare moments, but I will certainly have a look at xmlfuncs.au3. If I add any documentation, I'l pass it on.

Oh, BTW, if you add

__DebugMode()

after the include, you'll get lots of output in the scite console window. All the documentation I don't do I tend to make up for in debugging hooks :D Good luck!

Link to comment
Share on other sites

Is there a way to verify integrity of XML file? Just like when you open the .xml in IE it shows you up whether something is missing or wrong. I don't need to know what exactly is wrong (would be nice too) but just saying like ".XML is broken please fix it" :)

Also is there a way to get .xml to not Crash when special chars like some polish chars are used. For example just by adding "ł" inside the log_to_file breaks the .xml

<?xml version="1.0" encoding="utf-8"?>

<settings>

<basic_configuration>

<program_sett134223424234234ings>

<log_to_file>ł</log_to_file>

</program_sett134223424234234ings>

<program_sett1342234534234ings>

<log_to_file>1234</log_to_file>

</program_sett1342234534234ings>

</basic_configuration>

<basic_conf2345243guration>

<program_sett134234234ings>

<log_to_file>1234</log_to_file>

</program_sett134234234ings>

</basic_conf2345243guration>

<basic_configurat4423ion>

<program_sett134234234ings>

<log_to_file>1234</log_to_file>

</program_sett134234234ings>

</basic_configurat4423ion>

<bas12421342ic_configuration>

<program_sett134223424234234ings>

<log_to_file>1234</log_to_file>

</program_sett134223424234234ings>

</bas12421342ic_configuration>

</settings>

Hope someone got some idea how to fix those :)

My little company: Evotec (PL version: Evotec)

Link to comment
Share on other sites

Oh, BTW, if you add

__DebugMode()

after the include, you'll get lots of output in the scite console window. All the documentation I don't do I tend to make up for in debugging hooks :) Good luck!

Your example works perfectly and the _DebugMode is very helpful. I'm still on a steep learning curve, and I've come up against a problem.

In DITA there are actually four types of topic element: "topic," "concept," "task," and "reference." For the <dita> node these are the only children allowed, so I can easily select them all at once for processing, using the xpath selector "/dita/*".

However, from a topic node, "*" would return other children besides the four topic types. Once I'm processing a topic node, I need to use the xpath selector "topic | concept | task | reference" to find nested topics. In a recursive function, this would simply act on the current node and find the child nodes I want.

The trouble seems to be that, with _XMLSelectNodes, I can't issue an xpath selector relative to a context node. I have to refer to the root node: "/dita/topic | dita/concept | dita/task | dita/reference." If I want to write a recursive function that looks into each topic (or concept or whatever) for more topics (or concepts or whatever), I am going to have to build a fairly horrible xpath selector as I go.

Is this an accurate reading, or am I missing something? Is there a way forward, or is there anoither strategy that would work better with this toolkit? My goal is to burst the original file into many topic files, none of which contains a nested topic. (Then I can create customized nested structures from the topic files using a "ditamap.")

Edited by AJO
Link to comment
Share on other sites

The trouble seems to be that, with _XMLSelectNodes, I can't issue an xpath selector relative to a context node. I have to refer to the root node: "/dita/topic | dita/concept | dita/task | dita/reference." If I want to write a recursive function that looks into each topic (or concept or whatever) for more topics (or concepts or whatever), I am going to have to build a fairly horrible xpath selector as I go.

To save you the trouble of replying, I've worked out how to do it with the DOM. THe examples in _XMLDOMWrapper.au3 and XMLFuncs.au3 were very helpful.

Here is my sample file, and the code to date, complete with beginner comments. I still have to process the id and the filenames, but there I'm on familiar ground. (Note: for some reason, in Preview Post, the indentation is gone. I've tried pasting the files using tabs and using spaces, but it makes no difference. . .)

One puzzle: after processing Topic 1.2, the program returns to Topic 1 and writes it. I would expect to see Topics 1.1 and 1.2 included in the output for Topic 1, but they are gone. This is exactly what I want, but I don't understand it. I thought I would have to delete them after writing them.

CODE

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "dtd\ditabase.dtd" >

<dita>

<concept id="jo_001">

<title>Topic 1</title>

<conbody>

<p>supports a number of methods for data exchange with other systems</p>

</conbody>

<concept id="jo_002">

<title>Topic 1.1</title>

<conbody>

<p>The available procedures for importing and exporting information in</p>

</conbody>

</concept>

<task id="jo_009">

<title>Topic 1.2</title>

<taskbody>

<p>Some task instructions</p>

</taskbody>

</task>

</concept>

<concept id="jo_003">

<title>Topic 2</title>

<conbody>

<p>Whatever else</p>

</conbody>

</concept>

<task id="jo_004">

<title>Topic 3</title>

<taskbody>

<p>Some task instructions</p>

</taskbody>

<task id="jo_005">

<title>Topic 3.1</title>

<taskbody>

<p>Some other task instructions</p>

</taskbody>

<task id="jo_007">

<title>Topic 3.1.1</title>9

<taskbody>

<p>Some more task instructions</p>

</taskbody>

</task>

<task id="jo_008">

<title>Topic 3.1.2</title>

<taskbody>

<p>Some different task instructions</p>

</taskbody>

</task>

</task>

<task id="jo_006">

<title>Topic 3.2</title>

<taskbody>

<p>Some task instructions</p>

</taskbody>

</task>

</task>

<reference>

<title>Topic 4</title>

<refbody>

<p>Some task instructions. Topic 4 in the original has no id attribute.</p>

</refbody>

</reference>

</dita>

CODE

#cs ; ==========================================================================

AJO 2008-02-05 DITAburst.au3 "bursts" a DITA-like file with nested topics

into separate topic files, such that each topic file contains no nested topics.

Once I had the DOM methods figured out,

this took less than a couple of hours to write and debug.

#ce

; keep track of iterations for debugging

; Global $count = 0

; create an object for the input file:

$xmlDoc = ObjCreate("Msxml2.DOMdocument.3.0")

If Not(IsObj($xmlDoc)) Then

ConsoleWrite("Problem creating $xmlDoc." & @CRLF)

EndIf

; disable validation of this not-really-DITA file

; which nevertheless references a DTD:

$xmlDoc.async = False

$xmlDoc.validateOnParse = false

; may have to include or remove ProhibitDTD depending on MSXML version;

; works at the office, but at home I have to comment it out:

; $xmlDoc.setProperty ("ProhibitDTD",false)

; load the file into the object, where it becomes a tree:

$xmlDoc.load("test_2.xml")

If ($xmlDoc.parseError.errorCode <> 0) Then

$myErr = $xmlDoc.parseError;

ConsoleWrite("Parse error: " & $myErr.reason);

Else

; Obtain the <dita> node.

; First we get a list of nodes using the selectNodes method.

; In this case, there is only one node, the document element.

$nodelist = $xmlDoc.selectNodes("/dita")

; Then we specify the node (or "item") I want.

$ditanode = $nodelist.item(0)

; Now we are ready to process topic nodes:

_ProcessTopics($ditanode)

EndIf

#cs **********************************************************************

FUNCTION _ProcessTopics

#ce

Func _ProcessTopics($currentNode)

; $count = $count + 1

; ConsoleWrite("Entering _ProcessTopics, count is " & $count & @CRLF)

; Again, I select nodes to get a list, then pick each node or "item".

$nodelist = $currentNode.selectNodes("topic | concept | task | reference")

; If there are no topic nodes inside the current node,

; then we have found the kind of node we want to write to a file.

; Write the node to a file:

If $nodelist.length = 0 Then

; ConsoleWrite("Calling _WriteTopic because there are no child nodes." & @CRLF)

_WriteTopic($currentNode)

; But if there are topic nodes inside this node, then for every one of them,

; call this very function to process them:

Else

; I have to store the nodelist so that it is stable for recursions.

$nodelist_stored = $nodelist

; For each node, process the node for more nested topics.

For $i=0 to $nodelist_stored.length - 1

$thisnode = $nodelist_stored.item($i)

; ConsoleWrite("About to process the following node:" & @CRLF)

; ConsoleWrite($thisnode.xml & @CRLF)

; ConsoleWrite("Calling _ProcessTopics recursively. $i = " & $i & @CRLF)

_ProcessTopics($thisnode)

; ConsoleWrite("Returning from recursive call to process next node. $i = " & $i & @CRLF)

Next

; And when you're done that, write this node to a file,

; unless this is the DITA node itself.

If Not($currentNode.nodeName = "dita") Then

; ConsoleWrite("Calling _WriteTopic because we have exhausted the child topics in the current node." & @CRLF)

_WriteTopic($currentNode)

EndIf

EndIf

Return

EndFunc ; ---------- end of _ProcessTopics

#cs ********************************************************************

FUNCTION _WriteTopic

#ce

Func _WriteTopic($currentNode)

; ConsoleWrite("Entering _WriteTopic, count is " & $count & @CRLF)

; We get the id attribute

; (if the attribute is missing, returns an empty string):

$id = $currentNode.getattribute("id")

; ConsoleWrite("id attribute = " & $id & " ")

; If there's no attribute, add one

If $id = "" Then

; ****** We still need to calculate a random one! For now:

$id = "jo_new_001"

$currentNode.setattribute("id", $id)

EndIf

; We write the node to a file:

; create another object where we can assemble the output XML:

$xmlDoc2 = ObjCreate("Msxml2.DOMdocument.3.0")

If Not(IsObj($xmlDoc2)) Then

ConsoleWrite("Problem creating $xmlDoc2 on iteration" & $i & @CRLF)

EndIf

; define objects for various bits of XML, and add them to the object

; in which we are assembing the XML:

$objPI = $xmlDoc2.createProcessingInstruction ("xml", "version=""1.0"" encoding=""UTF-8""")

$xmlDoc2.appendChild ($objPI)

; eventually we'll need to add a DTD reference,

; but for now, leaving it out is probably a good idea.

; We can add one when we post-process the almost-DITA topic

; into true DITA.

$rootElement = $xmlDoc2.createElement ("dita")

; not sure why I need the next line, but I guess I do:

$xmlDoc2.documentElement = $rootElement

; not sure why I need the next line either, but I guess I do:

$root = $xmlDoc2.documentElement

; I wanted to leave the above two lines out and write:

; $rootElement.appendChild($thisnode)

; but it didn't work.

$root.appendChild($currentNode)

; We need a filename.

; $filename = "Topic" & $i & ".xml"

; In fact we will want the content of the <title> node as the filename.

; ***** We still have to do some checking to make sure it's unique, and some

; fixing if it isnt. but for now, let's get the string:

$titlenodes = $currentNode.selectNodes("title")

; we expect one title node only, but for debug:

; ConsoleWrite("Number of title nodes found: " & $titlenodes.length & @CRLF)

; get the first (and only) title node:

$titlenode = $titlenodes.item(0)

; get the text from the node:

$filename = $titlenode.text & ".xml"

ConsoleWrite("Writing " & $filename & @CRLF)

; here we need to process the title text, replacing spaces with underscores

; and ensuring that a file of the same name does not exist, etc.,

; but this is for another day.

; we may also want to record the level of the topic in the title,

; or even in a processing instruction.

; Save the assembled object to a file:

$xmlDoc2.save($filename)

; not sure if we can close or drop or destroy $xmlDoc2,

; or need to, before issuing the next ObjCreate command in the loop.

; Apparently it's not necessary

; I thought I would have to delete this topic so that it would not be

; included in the output of the parent topic,

; but for some reason I don't. That's fortunate, but I'd feel

; better if I understood why the child topics, which are indubitably

; inside the parent topic, do not show up when I write the parent topic.

; Where did they go?

Return

EndFunc

ConsoleWrite("Ending DITAburst.au3." & @CRLF & "Done." & @CRLF)

Link to comment
Share on other sites

Is there a way to verify integrity of XML file? Just like when you open the .xml in IE it shows you up whether something is missing or wrong. I don't need to know what exactly is wrong (would be nice too) but just saying like ".XML is broken please fix it" ;)

Also is there a way to get .xml to not Crash when special chars like some polish chars are used. For example just by adding "ł" inside the log_to_file breaks the .xml

Hope someone got some idea how to fix those :)

For validation, the XMLDOMWrapper functions include _XMLSchemaValidate. I have no experience with it, but I think you could use it to verify your XML file.

For special characters, use HTML Unicode entities (see http://theorem.ca/~mvcorks/code/charsets/auto.html). These are strings that use an ampersand (&), then a hash (#), then a number (eg 407), then a semicolon(;). For the character you want, try 407.

Edited by AJO
Link to comment
Share on other sites

I need help trying to get the value of "SpellID" in this xml file:

<?xml version="1.0"?>
<Keys>
  <Key KeyName="Test.Test2" BarState="Bar1" ShiftState="None" Char="1" SpellID="4562" />
</Keys>

There will be more than one node, can anyone help me?

Link to comment
Share on other sites

Larry - I don't use Vista but I found an example unattend file, it doesn't look complicated. It helps to know which parts you want to extract.

Sinister - Need more info. If you have a bunch of nodes with SpellId do you want to retrieve them all or do you want to retrieve one using a query?

Link to comment
Share on other sites

Larry - I don't use Vista but I found an example unattend file, it doesn't look complicated. It helps to know which parts you want to extract.

Sinister - Need more info. If you have a bunch of nodes with SpellId do you want to retrieve them all or do you want to retrieve one using a query?

I am trying to extract a UserName of a created user... it has some sort of namespace thing that I just don't get...

Lar.

f_mrcleansmalm_77ce002.jpgAutoIt has helped make me wealthy

Link to comment
Share on other sites

weaponx,

I want to retrieve them all

Here is a simple example:

#include <_XMLDomWrapper.au3>
#include <Array.au3>

$sXMLFile = "test4.xml"

$result = _XMLFileOpen($sXMLFile)
If $result = 0 Then Exit

$path = "//Keys"

;Retrieve all subnodes of "Keys"
$nodesArray = _XMLGetChildNodes($path)

;Retrieve all subnodes (same result as above)
;$nodesArray = _XMLSelectNodes($path & "/*")

;Retrieve all subnodes named "Key"
;$nodesArray = _XMLSelectNodes($path & "/Key")

_ArrayDisplay($nodesArray)

For $X = 1 to $nodesArray[0]
    ConsoleWrite(_XMLGetAttrib($path & "/Key[" & $X & "]", "SpellID") & @CRLF)
Next
Link to comment
Share on other sites

  • 2 weeks later...

I need some more help. When I use the following code to get the value of SpellID, I try writing to the same line, but it ends up writing to every single Node when I only want it to write to one single one.

For $X = 1 to $NodesArray[0]
        $CheckKey = CheckKey(_XMLGetAttrib($path & "/Key[" & $X & "]", "SpellID"))
        If $CheckKey = -1 Then
            MsgBox(16, "Error", "Unable to read process memory!")
            ExitLoop
        EndIf
        If $Bar <> "" Then
        $GetStatus = StringSplit($Bar, "|")
        If $GetStatus[0] = 2 Then
            _XMLSetAttrib($path & "/Key[@SpellID="&_XMLGetAttrib($path & "/Key[" & $X & "]", "SpellID")&"]", "BarState", $GetStatus[1])
            _XMLSetAttrib($path & "/Key[@SpellID="&_XMLGetAttrib($path & "/Key[" & $X & "]", "SpellID")&"]", "Char", $GetStatus[2])
        EndIf
        EndIf

                Next

Obviously there is something wrong with my _XMLSetAttrib() function

Link to comment
Share on other sites

You are overcomplicating this. Take the first example I posted for you and add this on the end:

;Set attribute for specific element (in the example we have 3 elements)

_XMLSetAttrib($path & "/Key[1]", "SpellID", 123456)

This will change the SpellID of the first key to 123456...

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...