Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Insert Links in Templates? #471

Open
alpha1125 opened this issue Jan 13, 2015 · 20 comments
Open

Insert Links in Templates? #471

alpha1125 opened this issue Jan 13, 2015 · 20 comments

Comments

@alpha1125
Copy link

alpha1125 commented Jan 13, 2015

Is there a way setValue for a link, and link text?


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

@yellow-sunshine
Copy link

I'd like to second this! Links are a major part of word documents to be left out of templates.

I am showing when uncompressing a docx file which already has a hyperlink in it, the document.xml file has the link text but no hyperlink present. Further investigation shows the hyperlink is not put into document.xml at all. All word hyperlinks are stored inside of the document.xml.rels file. This creates a problem when writing because phpword now has to write to another file within the docx archive. Editing the source to do this is above my expertise, it would be nice if someone with the knowledge to do so could resove this as it has been a request for some time now.

Anyone want to chime in? Anyone have ideas on how to make it work? setValue for existing templates is what we are trying to do... not create a new document and insert a link.

@yellow-sunshine
Copy link

I tried editing Template.php and it did not work for me. I created a link in a word template, then updated Temlpate.php with the following:
I added a variable to the class

private $_documentXML_rels;

Opened a second document in the __construct method

$this->_documentXML_rels = $this->_objZip->getFromName('word/_rels/document.xml.rels'); 

added the following to the setValue method

$replace_rel = preg_replace('testlink1', 'testlink2', $replace);
preg_match_all('/\$[^\$]+?}/', $this->_documentXML_rels, $matches_rel);
    for ($i=0;$i<count($matches_rel[0]);$i++){
        $matches_new_rel[$i] = preg_replace('/(<[^<]+?>)/','', $matches_rel[0][$i]);
        $this->_documentXML_rels = str_replace($matches_rel[0][$i],$matches_new_rel[$i],$this->_documentXML_rels);
    }
$this->_documentXML_rels = str_replace($search, $replace_rel, $this->_documentXML_rels);

And finnaly, I added this line to the save function to save the document

$this->_objZip->addFromString('word/document.xml', $this->_documentXML_rels);

All this did is corrupt the document. Anyone else want to give it a go?

@fhferreira
Copy link

+1

@ant-s
Copy link

ant-s commented Aug 14, 2017

@GBRENT Did you ever find a solution to this? I am experiencing the same problem now.

@yellow-sunshine
Copy link

Wish I did, I ended up printing out the link as text untill there is a solution. I tried everything, even hired another programmer to resolve it and he couldn't either. It looks like the tech just is not there. Another option I did is created a pdf with a link. But PHPword with a link is a no go at this time.

@ant-s
Copy link

ant-s commented Aug 15, 2017 via email

@ant-s
Copy link

ant-s commented Aug 15, 2017

I am attempting a crude version of this, essentially getting the current rels and adding in the new rIds for the links that were added during templating. I have the added complication of my links being in a table, which also has to be converted into a string to enable it to be used in a variable replacement.

@ant-s
Copy link

ant-s commented Aug 16, 2017

Ok, so I seem to have made this work by extending the TemplateProcessor class. Essentially what you need to do is update the relationships file when adding a link. Whenever you add a link you need to pass the resulting object to addLink like this.

$oLink = $oTextRun->addLink('http:https://www.google.com/', 'Hyperlink');
$template->addLink($oLink);

I have it even more complicated in my application as I am also having to replace variables with string XML like this:
#271

Code for extended processor.

/**
 * Extension of TemplateProcessor to add in new links.
 */

// namespace could be specified here

use PhpOffice\PhpWord\TemplateProcessor;

class LinkedTemplateProcessor extends TemplateProcessor {

  public function __construct($documentTemplate)
  {
    // Construct as normal for the parent class.
    parent::__construct($documentTemplate);

    // Get the current relationships from the 'rel' file.
    $this->tempDocumentRelationships = $this->zipClass->getFromName($this->getRelsName());

    // Create an array to store PHPWord links objects in.
    $this->links = array();

    // Store the current links in the template as an XML object.
    $this->xmlRelationships = simplexml_load_string($this->tempDocumentRelationships);

  }

  /**
   * Overwrite save() method to add in updating of relationships.
   *
   * @return string
   */
  public function save()
  {
    // Add in the updated relationships XML.
    $this->zipClass->addFromString($this->getRelsName(), $this->xmlRelationships->asXML());

    return parent::save();
  }


  /**
   * Add links to the link object.
   *
   * @param $link
   *   PHPWord link object
   */
  public function addLink($link) {
    // This is not tested, but I think there might not be an entry in the
    // relationships file for internal links.
    if ($link->isInternal()) {
      return;
    }

    // Set the relationship id number for the link.
    $next_rid = $this->getNextRid();

    // PHPWord adds 6 to the rId because it assumes we are starting
    // from a blank document that has the 6 default rIds in it.
    // We need to compensate for this.
    $link_rid_num = $next_rid - 6;

    // Set the relationship id for the PHPWord link object.
    $link->setRelationId($link_rid_num);

    // Add link as relationship in XML relationships.
    $xml_child = $this->xmlRelationships->addChild('Relationship');

    $xml_child->addAttribute('Id', 'rId' . $next_rid);
    $xml_child->addAttribute('Type', 'http:https://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink');
    $xml_child->addAttribute('Target', $link->getSource());
    $xml_child->addAttribute('TargetMode', 'External');

  }


  /**
   * Function to add multiple links.
   *
   * @param $link
   *   A PHPWord link object (returned when using addLink).
   *   These need to be passed to the template processor so the
   *   links relationship file can be updated.
   */
  public function addLinks($links) {
    foreach ($links as $link) {
      $this->addLink($link);
    }
  }


  /**
   * Return the number of the next Rid.
   *
   * @return int
   */
  public function getNextRid() {
    // Loop through the current links and get the latest number.
    $link_nums = array();
    foreach ($this->xmlRelationships as $xmlRelationship) {
      $attributes = $xmlRelationship->attributes();
      if (isset($attributes['Id'])) {
        if (substr($attributes['Id'], 0, 3) == 'rId') {
          $link_nums[] = intval(substr($attributes['Id'], 3));
        }
      }
    }

    return max($link_nums) + 1;

  }


  /**
   * Get the name of the rel file for $index.
   *
   * This is presumed to always be constant unlike footer parts that
   * can have sequential numbers.
   *
   * @return string
   */
  protected function getRelsName()
  {
    return 'word/_rels/document.xml.rels';
  }

}

If anyone has time this could be worked into the main template processor class. I'm not sure how you would fit the links together. It would be good to do at the same time as #271.

@japicoder
Copy link

For those of you who ended up here like me.
I have a template where I replace some placeholders with real values. The text usually contains some HTML, so I used \PhpOffice\PhpWord\Shared\Html::addHtml() to obtain TextRun elements with all the needed content. This can be embedded into the template with no problem, but the links were lost and rendered pointing to a wrong place.

After some debug I came to the conclusion that the library doesn't consider if there are references, taking it as a blank document, neither it updates the references when the new link is added.

To solve this, I extended the TemplateProcessor class by adding a similar method to ant-s addLink(), but based on the current version 0.17.

I call this method right after addHtml() does the conversion and I get everything working in my document.
Here's a gist with my class:
https://gist.github.com/japicoder/722e6d2435a9b2dfcd057730898d4a24

Hope it helps.

@soinalastudio
Copy link

Hello, can you tell how to use AddHtml to insert Html in the template please? Thabk you.
I'll test you AddLink function.

@japicoder
Copy link

Hi @soinalastudio, I needed some extra tweaks for that. I'll try to resume them:

  • The trick to insert HTML with addHtml() into a placeholder is to use a TableCell
  • Then, it's all about creating a new Table, adding a single Row to your table and finally, adding a Cell to the row.
  • Pass your table cell to the call addHtml($cell, $html, false); and you'll have it.

The problem then is how to assign a table object to a placeholder. The current version of the TemplateProcessor class doesn't allow it, but I found that the develop branch of the library has a setCustomComplexValue() method that we can use to replace a placeholder with a table. Then:

  • I extended the TemplateProcessor class to have a custom class and include this setCustomComplexValue() method and then in my code I just had to call it to assign the table to the placeholder.

@soinalastudio
Copy link

Thank you @japicoder , It's a little bit tricky... May be for now, I'll wait that we have a stable branch, I'll strip all tags and add in a block line by line with setValue. I have to deal also with the html validation as the html is entered by user. I see that the current version of addHtml doesn't accept even the
tag.

@soinalastudio
Copy link

Until now, I'm not able to Insert Links in the template. The document is broken when I use TextRun and addLink.

@japicoder
Copy link

Be aware of some characters that will broke your document. The & sign is a good example, because you have to convert it to its html entity: &
Also, ensure to sanitize your HTML text before calling addHTML, or you might get errors. That happened to me with a simple
that required to be
.
Check for   as it breaks the document too.

I know, it's hard sometimes, even more when it's about templates and placeholders, but don't give up. Good luck! :)

@soinalastudio
Copy link

soinalastudio commented Jul 7, 2020 via email

@paulomartinsask
Copy link

Hello,

I use phpword to export to LibreOffice and tried with the two options: addHtml() and addLink(), but after upgrading to LibreOffice 6.4 when exporting my document to PDF, the links stop working in the PDF. In LibreOffice the links work normally and if I create a link, through LibreOffice it works in the PDF. In the previous version of LibreOffice, 5.0, they work perfectly.
Has anyone ever experienced this?
Do you have any suggestions for solving this problem?

Thanks.

@alexisQuiroz
Copy link

Me sirvió de mucha ayuda el articulo!
gracias!

@github-actions
Copy link

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
If this is still an issue for you, please try to help by debugging it further and sharing your results.
Thank you for your contributions.

@github-actions github-actions bot added the Stale label Nov 18, 2022
@Progi1984 Progi1984 removed the Stale label Nov 18, 2022
@konradzuse
Copy link

this is a really important feature. I'm surprised this hasn't been implemented yet.

@xeux
Copy link

xeux commented May 11, 2024

In case someone needs this feature, I read in here you can insert OpenXML code, so this worked for me:

<w:r><w:fldChar w:fldCharType="begin"/></w:r><w:r><w:instrText xml:space="preserve"> HYPERLINK &quot;https://example.com&quot; </w:instrText></w:r><w:r><w:fldChar w:fldCharType="separate"/></w:r><w:r><w:t>Link text</w:t></w:r><w:r><w:fldChar w:fldCharType="end"/></w:r>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests