I want to ask, is it possible working with a template, and duplicate a specific page in that template without using WordFragment or addExternalFile (I want the template to be 1 file only)?
As an example, I have 3 page, I want to duplicate (or repeating) page 2 for 4 times, so the result is that I have a 7 paged document with a second page repeating for 4 times? Is it possible to do it without WordFragment or addExternalFile? Because I want to make it easier to edit in the template.
My Phpdocx is a trial version.
What I have tried so far is using wordfragment, by using $wordfragment$ variable in document template (in second page), and do this in php:
$wf = new WordFragment($docx, 'document');
for($i = 0: $<4; $i++) {
/*here I put a code to make a text and table in wordfragment (sorry it's too long to write it here, it's about 500 lines)*/
$wf->addBreak(array('type'=>'page')); //it's to go to next page
} //so this loop will make 4 same pages with the content I want in that 500 lines of code.
$docx->replaceVariableByWordFragment(array('wordfragment'=>$wf), array('type'=>'block'));
Using a WordFragment like above is actually very powerfull, and it is have a very great result, but since it will be used by my user, and we always presume that the user is not good with code, so I cannot let the user to edit the code if he/she just want to edit the template, in example, if the user want to edit the table in template, in this code of mine, that user need to edit it in that 500 lines codes, and that's very not user friendly, what I wanted is to make it more simple, make the text and table in word (so the user could just edit it in there), and in the code I just need to repeat that page. Is it possible?
Oh and the other method, addExternalFile is actually quite good, but by using this, I need to have more than 1 template, in example, I need 3 document, named start.docx, content.docx, end.docx. With addExternalFile, I could just used it like this:
$wf = new WordFragment($docx, 'document');
$wf->addExternalFile(array('src'=>'start.docx'));
$wf->addBreak(array('type'=>'page'));
for($i=0;$i<4;$i++) {
$content = new CreateDocxFromTemplate('content.docx');
/*here I put a code to replace all text and table variable in template*/
$content->replaceVariableByText($variables, $parameter); //$variables and $parameter already included in my code
$content->replaceTableVariable($table); //$table already included in my code
$content->createDocx('newcontent.docx');
$wf->addExternalFile(array('src'=>'newcontent.docx'));
$wf->addBreak(array('type'=>'page'));
} //this loop will make 4 same page
$wf->addExternalFile(array('src'=>'end.docx');
$docx->replaceVariableByWordFragment(array('wordfragment'=>$wf), array('type'=>'block'));
With addExternalFile, I could write text, variables, and draw a table in document and not in wordfragment. It's quite user friendly, but the flaw is that this method need atleast 3 different document to be opened by user if they want to edit it. What I want is just 1 document, all text, variables, and tables are in document, and there's no text or table in php code. Is there any method to do this with phpDocx?
Related
I am trying to create a module which has both frontend and backend functionality. Like I need to ask for the city in the home page when the store loads. And all the available cities are entered/managed in backend admin panel.
Before I used to write for only backend things, frontend seems little confusing.
There is a design folder which is completely for theme development.
All the example are little different(https://www.mageplaza.com/magento-2-module-development/,http://inchoo.net/magento-2/how-to-create-a-basic-module-in-magento-2/]2), they have routes.xml, where route_id, and all are defined, but here I don't need any extra route. Need some additional tweaks in frontend pages.
I created module V_name/M_name/adminhtml/block controllers etc view ...
Guide me how to create a module, which has both front end and backend connection, cities should be entered in admin, they should show on the frontend homepage.
For now, I only managed to edit home page content CMS page by adding some HTML which shows a popup with a dropdown for cities when the page loads.
Since you already have the back-end figured out I will focus on front-end. Also, since all you need to do is populate a list that you already have created this should be easy. I did something like this before and I found it easier to just use JSON to query a list of, in your case the cities, and populate the drop down. I don't believe this is the most 'MVP/proper' way to go, but it is less work then the other ways. (At least for me it is. I always prefer the JavaScript option since it allows for easy future page customization.)
To use the JSON method you need to create a Block with a method like the one below. You will see that you will also have to create a Resource Model (I'm not going to go over creating the Resource Model or the details of Blocks since there are much better resources than me already online that will go into every single detail you need.). Once this is complete you can access the data straight from the .phtml page in an easy to use JSON array.
First you need to make sure you are now structuring your Modules properly. The new Block below should be in a structure like this...
app/code/<VENDOR>/<MODULE>/Block/Wrapper.php (or whatever you name it)
The admin Blocks should be in the structure below, which it sounds like you are already know how to do.
app/code/<VENDOR>/<MODULE>/Block/Adminhtml
Create your Block and add a method to create a JOSN array like below...
public function getCityList()
{
$city_array = array();
/** #var \<VENDOR>\<MODULE>\Model\ResourceModel\City\Collection $collection */
$collection = $this->_cityCollectionFactory->create();
$collection->addFieldToFilter('active','1')->addFieldToSelect(['city_id', 'city']);
$collection->getSelect()->order(array('city ASC', 'city_id ASC'));
$count = 0;
foreach ($collection as $model)
{
$city_array["$count"] = $model->getData();
$count++;
}
return \Zend_Json::encode($city_array);
}
FYI... The foreach loop in the code above is weird and uses $count because I needed to do some tricky things to get something to work.
Then you can create the Block in your .phtml file to access the data via javascript.
<?php
$block_obj = $block->getLayout()->createBlock('<VENDOR>\<MODULE>\Block\Wrapper');
?>
<script type="text/javascript">
window.citylistJson = <?php echo $block_obj->getCityList() ?>;
</script>
The title is a bit convoluted. Let me explain better.
I'm making a blog where registered users can delete and edit their posts. The posts are displayed on 2 sites - the main page, and an admin control panel page. The code for generating the posts is in a separate file.
On the title page, everyone should only be able to see the posts. On the admin page, registered users can edit / delete their posts using the same included php file. There is one function smack in the middle of the code that determines if the editing buttons are visible. I want that function to be present when the file is included on the admin page, and not be present when it is included on the main page.
Let's say that the file which is included, blogposts.php looks something like this:
<?php
code
function();
rest of code
?>
What can i do to exclude that function in the instance where I don't need it?
I've also tried deleting the function and writing:
<?php
require "blogposts.php";
function();
?>
That executes it only on the last post, but should execute it on all of them since it is part of a while loop in the original code.
So, other than writing the code twice, once with and once without the function, if you guys have any ideas I'd appreciate it,
You could place the function inside an if statement, and check the current page against $_SERVER['REQUEST_URI'].
Something like:
if($_SERVER['REQUEST_URI'] == '/current_page_uri.php')
{
function();
}
And then replace the '/current_page_uri.php' with the URI for your admin page.
I'm just starting out using the ProcessWire system and really enjoying it.
On my Home Page, I would like to display an image from a random page. The page can be ANY page as long as the it is the child of the parent page with ID '1010'.
Is it possible, and if so, how do I achieve this?
My current code for showing the home page image is this:
if($page->image) echo "<img src='{$page->image->url}'>"; however, I'd like to select a random image from any of the children pages of the above parent ID.
I found this, but wasn't sure whether it would be of any use.
Many thanks for any pointers :-)
You should try something like this in your template's code (assuming that your image field is called image):
/* Find all children of page with ID 1010 that include an image */
$allChildPages = $pages->find('parent=1010,image.count>0');
/* Select a page from all children in the PageArray randomly */
$randomChildPage = $allChildPages->getRandom();
if ($randomChildPage->image) {
echo "<img src='{$randomChildPage->image->url}'>";
}
Have a look at the relevant code:
$pages->find() returns a collection of Pages (matching the Selector) as a PageArray (that extends the WireArray class).
$anyWireArray->getRandom() returns a random element of itself.
Also have a look at this forum thread where several strategies to randomize images from different pages are discussed.
I have inherited a concrete 5 project and am debugging why certain 'areas' on the page do not show up the 'inline editing' feature.
The version is 5.5.1.
Essentially there is a page where it lists some items and each item has some div's to contain some information about each one, e.g name, description:
<div class = "description"></div>
The issue is that only one of the description elements is editable - I have worked out that this could be because the 'area' is name statically thus:
<div class = "description">
$a = new Area('Property Details');
$cont = Page::getByID($page->getCollectionID());
$regexMatchThis = $a->display($cont);
</div>
So therefore there is only one area called 'Property Details' allowed to be editable. Am I ciorrect, and how can I name the area so that it reads the correct data but can be uniquely named so that it can be editable?
Help appreciated.
Pekka is correct, but I'm hesitant to litter the answer discussion with another (long) explanation.
Areas are dynamic based on the name. Thus, "Property Details 1" is different than "Property Details 01".
You could do something like:
for ($i = 1; $i <= 10; $i++) {
$a = new Area('Property Details ' + $i);
$a->display();
}
And you'd get 10 sequentially named "Property Details x" areas, from 1 - 10.
Let's say you add content block to them. As long as they remain with the exact same names, then the blocks will work as expected. You could even change the first line to:
for ($i = 10; $i >= 1; $i--) {
In that case, you'd get the areas named sequentially downward (10, 9, 8...) and C5 would keep the content blocks in their original areas -- so they'd all be in reverse.
But let's say you do:
for ($i = 11; $i <= 20; $i++) {
Now you get 10 areas (11, 12, 13...), and all are blank. The content blocks have basically disappeared. You can create new content blocks if you want. But then go ahead and put the original loop back in (1, 2, 3...). Your original content blocks are back -- just like before.
This really long explanation just goes to show that C5 creates a block based on a name, and that name becomes the key. It can be anything. You can base it on the page name, or dynamically generate it, or whatever. You just can't change it once it's been created (if you don't want to "lose" the blocks).
But... I'll agree with Pekka again here... you probably don't want to do this. Not knowing your goal, you're creating (or prolonging) a very brittle solution that's difficult to maintain. Pekka suggests creating subpages for each property, then you can use a page list block to "pull" the applicable attributes. Or, if you don't want to create separate pages, use Jordan's Designer Content block -- http://www.concrete5.org/marketplace/addons/designer-content/ . Have a single area ("Property Details"), and add a block for each property. Much easier to delete, reorder, etc.
Edit to address Sphere's first two questions:
Adding blocks to areas is really straightforward. As long as the areas don't have duplicate names, you click on it just like you've tried previously, and add a block. I'm not sure what block type your particular site needs -- that's specific to your site. It might just be a Content block, or maybe a Pagelist block with a particular template, where the previous developer set it up to "list" one page per block instance, per area (which would be REALLY weird, but you never know...). Or, maybe it's a block type created from the Designer Content block which I mentioned earlier. Poking around on the existing blocks should give you an idea. Also, I find that sometimes they write out some identifying ID in the HTML. So, the source is something like .
As for your code sample: Yes, $cont is page. The code that Pekka and I provided ($a->display()) basically does:
Initialize an area called 'Property Details' (or 1 or 2 or whatever). This is the key, as discussed.
Display it, defaulting to displaying it "for" the current page. This is different than "on" the current page. It will always display on the current page... what you pass as an argument to ->display() is more like "the page that the area should be pulled from". So if you could theoretically pass $thePageObjectForPropertyOnMainStreet, and it would display that page's 'Property Details' area. Using $cont is unnecessary. As it creating it (it already exists).
But... now that I think of it, that might be what he tried to do. $cont might have been a reference to individual property pages. Those pages might have an area called "Property Details", and this his loop serves to "pull the area" from those pages. But, $cont would have looked different.
You can hardcode this by doing something like $cont = Page::getByID(x);, where x is the cID for a property page, which has an area called "Property Details". You can get the cID in the URL after you edit and save it.
Yup, that code seems to be defining only one area (and doing some unrelated things in between).
If the second area you desire doesn't exist yet, just make up a second one like so:
<div class = "description">
<?php
$a = new Area('Some more info');
$a->display(); `
?>
</div>
Been using the plugin Custom Content Type Manager to create a custom post that displays location information.
We hold weekly games for each location - So what I'm trying to accomplish is in my custom post I have a set of checkboxes so you check if the venue is played on Monday, Tuesday or Wed...etc Then in my theme I'm going to have a 7 day calendar. And if a location is checked for that day then I want the title/links to the location printed there.
I'm giving you that background because I really dont think I'm going about this the correct way. Essentially I'm doing it in a loop, and I'm pulling all the checkbox options in an Array, and if the option is equal to Monday to that specific day, then it prints the locations title name etc.
I want this setup so a non-technical person (kinda like me lol) can just add a new location and pick "friday" for example and the code does the rest.
Essentially I got it working. 2 problems though
I'm running 7 loops to accomplish this - one for each day. I know this is stupid and there is probably a better solution.
It's printing the correct information - however its also reading/printing each of the other locations except its not putting up the info for them - I know this cause its creating empty DIVs for them.
NOTE: I'm having issues posting the whole code...?? I deleted all the php tags to present this
$weekly = new WP_Query( array( 'post_type' => 'locations', 'posts_per_page' => 5 ) );
while ( $weekly->have_posts() ) : $weekly->the_post();
<div class="weekly-venue-spacer">
$day_array = get_custom_field('weekly_day:to_array');
if (in_array('3', $day_array)) {
print_custom_field('venue_display_name');
echo "<br />";
print_custom_field('city_crossroads');
}
</div>
endwhile;
wp_reset_postdata();
the '3' in the in_array statement just means "Wednesday".
Here look at this image:
http://i40.tinypic.com/svnee0.jpg
an example of the empty DIVs being created - easily seen with padding applied to the div
Thanks for reading. Any solution to approach this differently would be great.
I am not sure I understood correctly what you want , but assuming I did - I think your whole approach is a bit wrong/complicated .
First of all, you do not need 7 loops .
I have noticed have a custom field - so in that custom field , instead of an array, just store the ONE day that you need , and then simply GET by checking the custom_field value ..
Second - why do you use checkbook and not a list ? is there an eventual event that can be in several different days ? because if every event is exclusive for one day - than it would be more easy to use a drop list or even radio buttons.
and for your direct question - I do not know how the value of the custom field is structured - but you are printing ALL of it ..
EDIT I : After reading comment and understanding better the problem -
While still thinking that the approach is a bit wrong , but not knowing exactly how you construct the data - I will address the IMMEDIATE problem :
The code creates empty DIVS simply because you tell it to .
you are using a WHILE condition in the code BEFORE outputting a div.
Since your query gets 5 posts - it will create 5 divs (some of which that do not meet the NEXT condition , will be of course empty).
your function now , put in human-words is working like this :
1. Get 5 post.
2. As long as I have posts (for each post), Open a div.
3. If you have Tuesday in array - print something
4. close div
5. if not finished all posts (in our case, 5) - go back to step 2.
It is obvious that the code will print empty div also for empty events..
So to get it right you simply move the opening div tag to BEFORE the WHILE condition .
that is if you do not need to check for existence of events in the query ..
The right way would be to use also the IF statement, just like the regular wordpress loop.
The general mechanism is this :
<?php if ($weekly->have_posts()) : ?>
//now we open a div
<?php while ( $weekly->have_posts() ) : $weekly->the_post();?>
// now we check for other conditions and print them if available.
<?php endwhile; ?>
// now we close the DIV
<?php endif; ?>