Completed
Push — master ( badd01...de655c )
by Nicolaas
02:31
created

CopyFactoryDataExtension::CopiedFromObject()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 3
nc 3
nop 0
1
<?php
2
3
4
class CopyFactoryDataExtension extends DataExtension
5
{
6
    public static function get_extra_config($className, $extension, $args)
7
    {
8
        //foreach($config as $name => $value) {
9
        //	Config::inst()->update($className, $name, $value);
10
        //}
11
        // Force all subclass DB caches to invalidate themselves since their db attribute is now expired
12
        //DataObject::reset();
13
        return array(
14
            'has_one' => array(
15
                "Copy".$className => $className,
16
                "Copy".$className."_Completed" => $className
17
            )
18
        );
19
    }
20
21
    public function updateCMSFields(FieldList $fields)
22
    {
23
        parent::updateCMSFields($fields);
24
        $className = $this->owner->ClassName;
0 ignored issues
show
Bug introduced by
The property ClassName does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
25
        $uncompletedField = $this->owner->CopyFromFieldName();
26
        $uncompletedFieldWithID = $uncompletedField."ID";
27
        $completedField = $this->owner->CopiedFromFieldName();
28
        $completedFieldWithID = $completedField."ID";
29
        //remove by default
30
        $fields->removeByName($uncompletedFieldWithID);
31
        $fields->removeByName($completedFieldWithID);
32
33
        if (
34
            $this->owner->exists() &&
35
            SiteConfig::current_site_config()->AllowCopyingOfRecords &&
0 ignored issues
show
Bug introduced by
The property AllowCopyingOfRecords does not seem to exist. Did you mean record?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
36
            Permission::check('ADMIN')
37
        ) {
38
            $changeMessage =
39
                "<p class=\"message good\">".
40
                    _t("CopyFactory.CHANGE_SETTINGS", "You can change the settings for copying in").
41
                    " <a href=\"/admin/settings/\">"._t("CopyFactory.SITE_CONFIG", "The Site Config (see Copy Tab)")."</a>. ".
42
                    _t("CopyFactory.TURN_OFF_WHEN_NOT_IN_USE", "It is recommended you turn off the copy facility when not in use, as it will slow down the CMS.")."
43
                </p>";
44
            //reload goes here ... @todo
45
            /*
46
            if($this->owner->ID && Session::get("CopyFactoryReload") == $this->owner->ID) {
47
                Session::set("CopyFactoryReload", 0);
48
                return Controller::curr()->redirectBack();
49
            }
50
            */
51
            if ($this->owner->$completedFieldWithID) {
52
                if ($obj = $this->owner->$completedField()) {
53
                    $fields->addFieldToTab(
54
                        "Root.Copy",
55
                        new ReadonlyField(
56
                            $completedField."_EXPLANATION",
57
                            _t("CopyFactory.COPIED_FROM", "This record has been copied from: "),
58
                            $this->owner->CopyFactoryTitleMaker($obj)
59
                        )
60
                    );
61
                }
62
            } elseif ($situation = SiteConfig::current_site_config()->AllowCopyingOfRecords) {
0 ignored issues
show
Bug introduced by
The property AllowCopyingOfRecords does not seem to exist. Did you mean record?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
63
                if ($situation == 1) {
64
                    $message = _t(
65
                        'CopyFactory.DRY_RUN_ONLY',
66
                        "Dry run only --- any changes below will be tested once your press 'SAVE' but no actual changes will be made.  You will find a log of intended changes below for review."
67
                    );
68
                }
69
                if ($situation == 2) {
70
                    $message = _t(
71
                        'CopyFactory.THIS_IS_FOR_REAL',
72
                        "Any changes below will be actioned once you press 'SAVE' - please use with care."
73
                    );
74
                }
75
                $fields->addFieldToTab(
76
                    "Root.Copy",
77
                    $copyField = new LiteralField(
78
                        $uncompletedFieldWithID."_WARNING",
79
                        "<p class=\"warning message\">".$message."</p>".
0 ignored issues
show
Bug introduced by
The variable $message does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
80
                        $changeMessage
81
                    )
82
                );
83
                $copyableObjects = $className::get()
84
                    ->exclude(array("ID" => intval($this->owner->ID) - 0))
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
85
                    ->filter(array("ClassName" => $this->owner->ClassName));
86
                if ($this->owner->hasMethod("additionalFiltersForCopyableObjects")) {
87
                    $copyAbleObjects = $this->owner->additionalFiltersForCopyableObjects($copyableObjects);
0 ignored issues
show
Unused Code introduced by
$copyAbleObjects is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
88
                }
89
                //there are objects to copy from
90
                if ($copyableObjects->count() > 0) {
91
                    $fields->addFieldToTab(
92
                        "Root.Copy",
93
                        $copyField = new DropdownField(
94
                            $uncompletedFieldWithID,
95
                            _t(
96
                                'CopyFactory.COPY_EXPLANATION',
97
                                "Copy from {name}. CAREFUL - this will replace everything in the current {name} with the one copied from ...",
98
                                'Explanation on how copying works',
99
                                array('name' => $this->owner->i18n_singular_name())
0 ignored issues
show
Documentation introduced by
array('name' => $this->o...->i18n_singular_name()) is of type array<string,?,{"name":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
100
                            ),
101
                            $copyableObjects->map("ID", CopyFactory::preferred_title_field($this->owner))
102
                        )
103
                    );
104
                    $copyField->setEmptyString(_t("CopyFactory.SELECT_ONE", "--- Select One ---"));
105
                } else {
106
                    $fields->addFieldToTab(
107
                        "Root.Copy",
108
                        $copyField = new LiteralField(
109
                            $uncompletedFieldWithID."_EXPLANATION",
110
                            "<h2>".
111
                            _t(
112
                                'CopyFactory.COPY_FACTORY_HELP_NO_RECORDS',
113
                                "There are no records to copy from."
114
                            ).
115
                            "</h2>"
116
                        )
117
                    );
118
                }
119
            } else {
120
                $fields->addFieldToTab(
121
                    "Root.Copy",
122
                    $copyField = new LiteralField(
123
                        "CopyFactoryNotTurnedOn",
124
                        "<h2>".
125
                        _t(
126
                            'CopyFactory.COPY_FACTORY_TURNED_OFF',
127
                            "Copying of records is currently turned off."
128
                        ).
129
                        "</h2>".
130
                        $changeMessage
131
                    )
132
                );
133
            }
134
            if (Config::inst()->get("CopyFactory", "debug")) {
135
                $source = CopyFactoryLog::get()
136
                    ->filter(array("CopyCausingClassName" => $this->owner->ClassName, "CopyCausingClassNameID" => $this->owner->ID))
137
                    ->exclude(array("CopyIntoClassName" => $this->owner->ClassName, "CopyIntoClassNameID" => $this->owner->ID))
138
                    ->exclude(array("CopyIntoClassName" => $this->owner->ClassName, "CopyFromClassNameID" => $this->owner->ID));
139
                if ($source->count()) {
140
                    $name = "COPY_CAUSING_GRIDFIELD";
141
                    $title = _t("CopyFactory.COPY_CAUSING_TITLE", "Copy actions originated from this record.");
142
                    $fields->addFieldToTab("Root.Copy", $this->gridFieldMaker($name, $title, $source));
143
                }
144
                $source = CopyFactoryLog::get()
145
                    ->filter(array("CopyIntoClassName" => $this->owner->ClassName, "CopyIntoClassNameID" => $this->owner->ID))
146
                    //->exclude(array("CopyCausingClassName" => $this->owner->ClassName, "CopyCausingClassNameID" => $this->owner->ID))
147
                    ->exclude(array("CopyIntoClassName" => $this->owner->ClassName, "CopyFromClassNameID" => $this->owner->ID));
148
                if ($source->count()) {
149
                    $name = "COPY_INTO_GRIDFIELD";
150
                    $title = _t("CopyFactory.COPY_INTO_TITLE", "Copy actioned into this record.");
151
                    $fields->addFieldToTab("Root.Copy", $this->gridFieldMaker($name, $title, $source));
152
                }
153
                $source = CopyFactoryLog::get()
154
                    ->filter(array("CopyIntoClassName" => $this->owner->ClassName, "CopyFromClassNameID" => $this->owner->ID))
155
                    ->exclude(array("CopyIntoClassName" => $this->owner->ClassName, "CopyIntoClassNameID" => $this->owner->ID))
156
                    ->exclude(array("CopyCausingClassName" => $this->owner->ClassName, "CopyCausingClassNameID" => $this->owner->ID));
157
                if ($source->count()) {
158
                    $name = "COPY_FROM_GRIDFIELD";
159
                    $title = _t("CopyFactory.COPY_FROM_TITLE", "Copy actions from this record into another record.");
160
                    $fields->addFieldToTab("Root.Copy", $this->gridFieldMaker($name, $title, $source));
161
                }
162
            }
163
        } else {
164
        }
165
    }
166
167
    /**
168
     * @param String $name
169
     * @param String $title
170
     * @param DataList $source
171
     *
172
     * @return GridField
173
     */
174
    private function gridFieldMaker($name, $title, $source)
175
    {
176
        return new GridField(
177
            $name,
178
            _t("CopyFactory.COPY_FACTORY_LOG", "Copy Log: ").$title,
179
            $source,
180
            GridFieldConfig_RecordViewer::create(30)
181
        );
182
    }
183
184
    /**
185
     * The field that indicate where the object shall be copied FROM
186
     * note the future tense.
187
     * @return String
188
     */
189 View Code Duplication
    public function CopyFromFieldName($withID = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
190
    {
191
        $str = Config::inst()->get("CopyFactory", "copy_fields_prefix").
192
            $this->findOriginalObjectClassName();
193
        if ($withID) {
194
            $str .= "ID";
195
        }
196
        return $str;
197
    }
198
199
    /**
200
     * The field that indicates where the object was copied FROM
201
     * note the past tense ...
202
     * (links to "parent" object)
203
     * @return String
204
     */
205 View Code Duplication
    public function CopiedFromFieldName($withID = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
    {
207
        $str = Config::inst()->get("CopyFactory", "copy_fields_prefix").
208
            $this->findOriginalObjectClassName().
209
            Config::inst()->get("CopyFactory", "completed_field_appendix");
210
        if ($withID) {
211
            $str .= "ID";
212
        }
213
        return $str;
214
    }
215
216
    /**
217
     *
218
     * @var array of DataObject
219
     */
220
    private static $my_original_object = array();
221
222
    /**
223
     * finds the class name for the object
224
     * being copied in terms of the exact object being
225
     * extended by CopyFactoryDataExtension
226
     * @return string
227
     */
228
    private function findOriginalObjectClassName()
229
    {
230
        $key = $this->owner->ClassName.$this->owner->ID;
0 ignored issues
show
Bug introduced by
The property ClassName does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
Bug introduced by
The property ID does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
231
        if (!isset(self::$my_original_object[$key])) {
232
            $obj = $this->owner;
233
            while ($obj->hasExtension("CopyFactoryDataExtension")) {
234
                $finalObject = $obj;
235
                $obj = Injector::inst()->get(get_parent_class($obj));
236
            }
237
            self::$my_original_object[$key] = $finalObject;
0 ignored issues
show
Bug introduced by
The variable $finalObject does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
238
        }
239
        return self::$my_original_object[$key]->ClassName;
240
    }
241
242
243
    /**
244
     * provides a meaningful title for an object
245
     *
246
     * @param String $obj - the object you want the name for ...
247
     *
248
     * @return String ...
249
     */
250 View Code Duplication
    public function CopyFactoryTitleMaker($obj)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
251
    {
252
        $methodOrField = $this->CopyFactoryPreferredTitleField();
253
        if ($obj->hasMethod($methodOrField)) {
0 ignored issues
show
Bug introduced by
The method hasMethod cannot be called on $obj (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
254
            return $obj->$methodOrField();
0 ignored issues
show
Bug introduced by
The method $methodOrField cannot be called on $obj (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
255
        } elseif ($obj->hasMethod("get".$methodOrField)) {
0 ignored issues
show
Bug introduced by
The method hasMethod cannot be called on $obj (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
256
            $methodName = "get".$methodOrField;
257
            return $obj->$methodName();
0 ignored issues
show
Bug introduced by
The method $methodName cannot be called on $obj (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
258
        } else {
259
            return $obj->$methodOrField;
260
        }
261
    }
262
263
    /**
264
     *
265
     * @return String
266
     */
267
    public function CopyFactoryPreferredTitleField()
268
    {
269
        $titleMap = Config::inst()->get("CopyFactory", "title_map_for_display_of_record_name");
270
        if (isset($titleMap[$this->owner->ClassName])) {
271
            return $titleMap[$this->owner->ClassName];
0 ignored issues
show
Bug introduced by
The property ClassName does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
272
        }
273
        return "Title";
274
    }
275
276
    /**
277
     *
278
     * @return String
279
     */
280
    protected function getCopyFactorySessionName()
281
    {
282
        return
283
            Config::inst()->get("CopyFactory", "dry_run_for_session_base_name")
284
            ."_".
285
            implode("_", array($this->ClassName, $this->ID));
0 ignored issues
show
Bug introduced by
The property ClassName does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property ID does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
286
    }
287
288
    /**
289
     * mark that we are doing a copy ...
290
     */
291
    public function onBeforeWrite()
292
    {
293
        parent::onBeforeWrite();
294
        if (isset($this->owner->ID) && $this->owner->ID) {
295
            $fieldNameWithID = $this->owner->CopyFromFieldName(true);
296
            if (isset($_POST[$fieldNameWithID]) && $_POST[$fieldNameWithID]) {
297
                Session::set("CopyFactoryReload", $this->owner->ID);
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
298
            }
299
        }
300
    }
301
302
    /**
303
     * we run the actual copying onAfterWrite
304
     */
305
    public function onAfterWrite()
306
    {
307
        parent::onAfterWrite();
308
        if (SiteConfig::current_site_config()->AllowCopyingOfRecords) {
0 ignored issues
show
Bug introduced by
The property AllowCopyingOfRecords does not seem to exist. Did you mean record?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
309
            $fieldName = $this->owner->CopyFromFieldName(false);
310
            $fieldNameWithID = $this->owner->CopyFromFieldName(true);
311
            if ($this->owner->$fieldNameWithID) {
312
                if ($copyFrom = $this->owner->$fieldName()) {
313
                    $factory = CopyFactory::create($this->owner);
314
                    $factory->copyObject($copyFrom, $this->owner);
0 ignored issues
show
Compatibility introduced by
$this->owner of type object<SS_Object> is not a sub-type of object<DataObject>. It seems like you assume a child class of the class SS_Object to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
315
                } else {
316
                    // a little cleanup: lets reset ...
317
                    $this->owner->$fieldNameWithID = 0;
318
                    $this->owner->write();
319
                }
320
            }
321
        }
322
    }
323
324
    public function CopiedFromObject()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
325
    {
326
        if (
327
            $this->owner->exists()
328
        ) {
329
            $completedField = $this->owner->CopiedFromFieldName();
330
            $completedFieldWithID = $completedField."ID";
331
            if ($this->owner->$completedFieldWithID) {
332
                return $this->owner->$completedField();
333
            }
334
        }
335
    }
336
}
337